package com.formina.stars;

import java.util.HashMap;
import java.util.Random;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import com.formina.engine.Engine;
import com.formina.engine.GameRenderer;
import com.formina.engine.Model;
import com.formina.engine.ObjReader;
import com.formina.engine.Pointer;
import com.formina.stars.Confetti;
import com.formina.stars.R;
import com.formina.stars.Star;

import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;


public class StarRenderer extends GameRenderer {
	
	
    
    //GAME ONLY
    private int inc = 0;
    private float curScale = 0.1f;
    private boolean fingerUp = true;
    private int mode= 0;
    
	/** This is a handle to our per-vertex cube shading program. */
	private int mPerVertexProgramHandle;
		
	/** This is a handle to our light point program. */
	private int mPointProgramHandle;	

	
	 public static final int MODE_NONE = 0;
	    public static final int MODE_ADDING=1;
	    public static final int MODE_DESTROY=2;
	    public static final int MODE_FINGER_UP = 3;
	
	private Star lastStar = null;
	int start = 0;
	
	 protected String GetVertexShader(){
	    	return
	    	"uniform mat4 u_MVPMatrix;      \n"		// A constant representing the combined model/view/projection matrix.
	  		+ "uniform mat4 u_MVMatrix;       \n"		// A constant representing the combined model/view matrix.	
	  		+ "uniform vec3 u_LightPos;       \n"	    // The position of the light in eye space.	
	  		+ "attribute vec4 a_Color;        \n"     // Per-vertex color information we will pass in.
	  		+ "attribute vec4 a_Position;     \n"     // Per-vertex position information we will pass in.
	  		+ "attribute vec3 a_Normal;       \n"		// Per-vertex normal information we will pass in.

	    	+ "varying vec4 v_Color;          \n"     // This will be passed into the fragment shader.	 
	    	+ "void main()                    \n"     // The entry point for our vertex shader.
	    	+ "{                              \n"
			// Transform the vertex into eye space.
			  + "   vec3 modelViewVertex = vec3(u_MVMatrix * a_Position);              \n"
			// Transform the normal's orientation into eye space.
			  + "   vec3 modelViewNormal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));     \n"
			// Will be used for attenuation.
			  + "   float distance = length(u_LightPos - modelViewVertex);             \n"
			// Get a lighting direction vector from the light to the vertex.
			  + "   vec3 lightVector = normalize(u_LightPos - modelViewVertex);        \n"
			// Calculate the dot product of the light vector and vertex normal. If the normal and light vector are
			// pointing in the same direction then it will get max illumination.
			  + "   float diffuse = max(dot(modelViewNormal, lightVector), 0.1);       \n" 	  		  													  
			// Attenuate the light based on distance.
			  + "   diffuse = diffuse * (1.0 / (1.0 + (0.25 * distance * distance)));  \n"
			  + "   diffuse = diffuse * 90000.0;  \n"
			// Multiply the color by the illumination level. It will be interpolated across the triangle.
			  + "   v_Color = a_Color * diffuse;                                       \n" 	 
			// gl_Position is a special variable used to store the final position.
			// Multiply the vertex by the matrix to get the final point in normalized screen coordinates.		
			  + "   gl_Position = u_MVPMatrix * a_Position;                            \n"     
			  + "}  \n"; 
	    }
	 protected String PhongVertexShader(){
		 return
				 "uniform mat4 u_MVPMatrix;\n"+
				 "uniform mat4 u_MVMatrix;\n"+
				 "attribute vec4 a_Position;\n"+
				 "attribute vec3 a_Normal;\n"+
				 "attribute vec4 a_Color;\n"+
				 "attribute vec3 a_LightPosition;\n"+
				 "varying vec3 v_Normal;\n"+
				 "varying vec3 v_Pos;\n"+
				 "varying vec4 v_Color;\n"+
				 "varying vec3 v_LightPosition;\n"+
				 "void main() {\n"+
				 "gl_Position = u_MVPMatrix * a_Position;\n"+
				 "v_Normal = mat3(u_MVMatrix) * a_Normal;\n"+
				 "v_Pos=vec3(u_MVMatrix * a_Position);\n"+
				 "v_Color=a_Color;\n"+
				 "v_LightPosition=a_LightPosition;"+
				 "}";

	 }
	 protected String PhongFragmentShader(){
	    	return 
	    	"precision mediump float;\n"+
			"varying vec3 v_Normal;\n"+
			"varying vec3 v_Pos;\n"+
			"varying vec4 v_Color;\n" +
			"varying vec3 v_LightPosition;\n"+
			"const vec3 lightPos = vec3(0.0, 0.0, 5.0);\n"+
			"const float shininess = 60.0;\n"+
			"const vec3 specularColor = vec3(1.0, 1.0, 1.0);\n"+
			"void main() {\n"+
				"vec3 n = normalize(v_Normal);\n"+
				"vec3 v = normalize(-v_Pos);\n"+
				"vec3 l = normalize(v_LightPosition - v_Pos);\n"+
				"vec3 h = normalize(v + l);\n"+
				"float diff = max(1.0, dot(n, l));\n"+
				"float spec = pow(dot(n, h), shininess);\n"+
				"float edge = 1.0 - abs(dot(v, n)); edge = edge*edge;\n"+
				"gl_FragColor = vec4(v_Color*diff +  v_Color*spec + vec4(edge));\n"+
				//"gl_FragColor = vec4(v_Color*diff +  v_Color*spec + vec3(edge), 1.0);\n"+
				//"gl_FragColor = vec4(1.0,0.0,0.0, 1.0);\n"+
	    	"}";
	    }
	
	public StarRenderer(){
		
	}
	
    float angleInDegrees;  

	@Override
	public void onDrawFrame(GL10 gl) {
		loopStart = System.currentTimeMillis();
		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);	   

	    GLES20.glUseProgram(mPerVertexProgramHandle);

		
		// Set program handles. These will later be used to pass in values to the program.
		Engine.mMVPMatrixHandle = GLES20.glGetUniformLocation(mPerVertexProgramHandle, "u_MVPMatrix");
		Engine.mMVMatrixHandle = GLES20.glGetUniformLocation(mPerVertexProgramHandle, "u_MVMatrix"); 
		//Engine.mLightPosHandle = GLES20.glGetUniformLocation(mPerVertexProgramHandle, "u_LightPos");
		Engine.mPositionHandle = GLES20.glGetAttribLocation(mPerVertexProgramHandle, "a_Position");
		Engine.mColorHandle = GLES20.glGetAttribLocation(mPerVertexProgramHandle, "a_Color");
		Engine.mNormalHandle = GLES20.glGetAttribLocation(mPerVertexProgramHandle, "a_Normal"); 
		Engine.mLightPositionHandle = GLES20.glGetAttribLocation(mPerVertexProgramHandle, "a_LightPosition");
	 
		//lighting
		this.angleInDegrees++;
		if(angleInDegrees>359){
			angleInDegrees = 0;
		}
		Matrix.setIdentityM(Engine.mLightModelMatrix, 0);
        Matrix.translateM(Engine.mLightModelMatrix, 0, Width/2, Height/2, 300.0f);      
        Matrix.rotateM(Engine.mLightModelMatrix, 0, angleInDegrees, 0.0f, 1.0f, 0.0f);
        Matrix.translateM(Engine.mLightModelMatrix, 0, 1.0f, 1.0f, 1.0f);
        Matrix.multiplyMV(Engine.mLightPosInWorldSpace, 0, Engine.mLightModelMatrix, 0, Engine.mLightPosInModelSpace, 0);
        Matrix.multiplyMV(Engine.mLightPosInEyeSpace, 0, Engine.mVMatrix, 0, Engine.mLightPosInWorldSpace, 0); 
		//end lighting
        
		DrawModels(gl);
		// Draw a point to indicate the light.
        GLES20.glUseProgram(mPointProgramHandle);        
        
		MakeComets(gl);

        
        //drawLight();
        
		loopEnd = System.currentTimeMillis();
	    loopRunTime = ((loopEnd - loopStart));
	    try {
            if (loopRunTime < Engine.GAME_THREAD_FPS_SLEEP){
                    Thread.sleep(Engine.GAME_THREAD_FPS_SLEEP - loopRunTime);
            }
	    } catch (InterruptedException e) {
            // TODO Auto-generated catch block
	    	e.printStackTrace();
	    }
    
	}
	
	
	float Width,Height;
	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		// TODO Auto-generated method stub
		Height=height;Width=width;
		GLES20.glViewport(0, 0, width,height);
		Engine.HEIGHT=height; Engine.WIDTH=width;
		
		final float ratio = (float) width / height;
	    final float left = -ratio;
	    final float right = ratio;
	    final float bottom = -1.0f;
	    final float top = 1.0f;
	    final float near = 1.0f;
	    final float far = 100.0f;
	    Matrix.orthoM(Engine.mProjMatrix, 0, 0, (float)width, (float)height, 0,-1000f,1000f);
	    //gl.glOrthof(0, (float)width, (float)height, 0,-1000f,1000f);
		//Matrix.frustumM(Engine.mProjMatrix, 0, left, right, bottom, top, near, far);
        Matrix.setLookAtM(Engine.mVMatrix, 0, 0, 0f, 1f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
       
        //FillScreen();
        
    }

	
	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		// TODO Auto-generated method stub
		soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);
	    soundPoolMap = new HashMap<Integer, Integer>();
	    soundPoolMap.put(0, soundPool.load(Engine.CONTEXT, R.raw.jump, 1));
	    soundPoolMap.put(1, soundPool.load(Engine.CONTEXT, R.raw.popup, 1));
	    soundPoolMap.put(2, soundPool.load(Engine.CONTEXT, R.raw.fireworks, 1));
	    soundPoolMap.put(3, soundPool.load(Engine.CONTEXT, R.raw.firework2, 1));
	     
		GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		GLES20.glClearDepthf(1.0f);
		

		//shader init
		
		// Load in the vertex shader.
		vertexShaderHandle = compileShader(GLES20.GL_VERTEX_SHADER, this.PhongVertexShader());	
		fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, this.PhongFragmentShader());
		 
		mPerVertexProgramHandle = CreateAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, 
				new String[] {"a_Position",  "a_Color", "a_Normal", "a_LightPosition"});			

		programHandle = GLES20.glCreateProgram();             // create empty OpenGL Program

		// Define a simple shader program for our point.
        final String pointVertexShader =
        	"uniform mat4 u_MVPMatrix;      \n"		
          +	"attribute vec4 a_Position;     \n"		
          + "void main()                    \n"
          + "{                              \n"
          + "   gl_Position = u_MVPMatrix   \n"
          + "               * a_Position;   \n"
          + "   gl_PointSize = 5.0;         \n"
          + "}                              \n";
        
        final String pointFragmentShader = 
        	"precision mediump float;       \n"					          
          + "void main()                    \n"
          + "{                              \n"
          + "   gl_FragColor = vec4(1.0,    \n" 
          + "   1.0, 1.0, 1.0);             \n"
          + "}                              \n";
        
        final int pointVertexShaderHandle = compileShader(GLES20.GL_VERTEX_SHADER, pointVertexShader);
        final int pointFragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, pointFragmentShader);
        mPointProgramHandle = CreateAndLinkProgram(pointVertexShaderHandle, pointFragmentShaderHandle, 
        		new String[] {"a_Position"});   
        
	         
		Engine.Star = ObjReader.ReadObj("cstar.obj");
		Engine.StarParticle = ObjReader.ReadObj("star.obj");	
		Engine.Star.Bind();
		//Engine.StarParticle.FixNormals();
		Engine.StarParticle.Bind();
		
	}	 
	

	void MakeComets(GL10 gl){
		Random rand = new Random();
		//if(comets < this.maxComets){
			if(rand.nextInt(1000)<5){ //nextInt(1000)
				Comet comet = new Comet();
				float tempscale = 15f;
				float x, y, z;
				z = 0f;
				//figure out staring point
				//get random number to decide
				//which side to start from
				//0 top, 1 bottom, 2 left, 3 right
				int side = rand.nextInt(4);
				
				switch(side){
				case 0:	//top
					y = 0; //0
					x = rand.nextInt((int)Width);
					comet = new Comet(new float[]{x,y,z},new float[]{tempscale,tempscale,tempscale});
					comet.setDirection(0 + rand.nextInt(180));
					break;
				case 1: //bottom
					y = Height;
					x = rand.nextInt((int)Width);
					comet = new Comet(new float[]{x,y,z},new float[]{tempscale,tempscale,tempscale});
					float angle = 360- rand.nextInt(180);
					
					comet.setDirection(angle);
					break;
				case 2: //left
					y = rand.nextInt((int)Height);
					x = 0;
					comet = new Comet(new float[]{x,y,z},new float[]{tempscale,tempscale,tempscale});
					float angle1 =270 + rand.nextInt(180);
					if(angle1 > 360){
						angle1-=360;
					}
					comet.setDirection(angle1);
					
					break;
				case 3:
					y = rand.nextInt((int)Height);
					x = Width;
					comet = new Comet(new float[]{x,y,z},new float[]{tempscale,tempscale,tempscale});
					comet.setDirection(90 + rand.nextInt(180));
					break;
				default:
					break;
				}
				Engine.Comets.add(comet);
				Log.d("COMMET", "COMMET");
			}
		//}
		
	}
	
private boolean Adding(Pointer p){
		
		float size = .55f;
		float tempsize = size + (p.Inc  * curScale);
		
		Star s = new Star(new float[]{p.x, p.y, 0f},new float[]{tempsize,tempsize,tempsize});
		s.setZ(tempsize+s.Width());
		
		if(p.LastStar!=null){
			if(p.LastStar.CheckTQCollision(s)){
				return false; //collided do nothing
			}
		}
		p.Mode = MODE_ADDING;
		size = tempsize;
		Engine.Stars.add(s);
		p.LastStar = s;
		p.Inc++;
		this.playSound(0);
		
		return true;
	}
	
	private boolean Destroy(Pointer p){
		for(int i= Engine.Stars.size()-1; i>=0;i--){
			if (Engine.Stars.get(i).CheckTouch(p.x,p.y)){
				p.Mode = MODE_DESTROY;
				DestroyStar((Star)Engine.Stars.get(i));
				return true;
			}
		}
		return false;
	}
	
	private void DestroyStar(Star star){
		Engine.Effects.add(new Confetti(star.Location(), star.Scale()));
		Engine.Stars.remove(star);
		Random rand = new Random();
		int sound =-1;
		sound = rand.nextInt(2);
		this.playSound(sound+2);
	}
	
	public void playSound(int sound) {
	    /* Updated: The next 4 lines calculate the current volume in a scale of 0.0 to 1.0 */
	    AudioManager mgr = (AudioManager)Engine.CONTEXT.getSystemService(Context.AUDIO_SERVICE);
	    float streamVolumeCurrent = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
	    float streamVolumeMax = mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);    
	    float volume = streamVolumeCurrent / streamVolumeMax;
	    
	    /* Play the sound with the correct volume */
	    soundPool.play(soundPoolMap.get(sound), volume, volume, 1, 0, 1f);     
	}
	
	public void setColor(int r, int g, int b, boolean useRandom){
		Log.d("COLOR", String.valueOf(r));
		Engine.UseRandomColors = useRandom;
		Engine.DefaultColor = new float[]{(float)r/255f,(float)g/255f,(float)b/255f,1f};
	}

	
	void DrawModels(GL10 gl){
		
			
			for(int x= Engine.Stars.size()-1; x>=0;x--){
				Engine.Stars.get(x).update(loopStart);
			}
						
			for(int x= 0; x < Engine.Stars.size();x++){
				Engine.Stars.get(x).draw(gl);
			}
			
			for(int x= Engine.Comets.size()-1; x>=0;x--){
				Comet c = (Comet)Engine.Comets.get(x);
				if(c.IsDead()){
					Engine.Comets.remove(x);
				}else{
					c.update(loopStart);
				}
			}
			
						
			for(int x= 0; x < Engine.Comets.size();x++){
				Engine.Comets.get(x).draw(gl);
			}

			//check collision with stars
			   for(int x= Engine.Comets.size()-1; x>=0;x--){
				   Comet c = (Comet)Engine.Comets.get(x);
				   for(int y= Engine.Stars.size()-1; y>=0;y--){

					if(Engine.Stars.get(y).CheckCollision(c)){
						Engine.Stars.get(y).setIsDead(true);
						DestroyStar((Star)Engine.Stars.get(y));
					}
				   }
			   }

			
			for(int x= Engine.Effects.size()-1; x>=0;x--){
				Engine.Effects.get(x).update(loopStart);
				if(Engine.Effects.get(x).IsDead()){
					Engine.Effects.remove(x);
				}
			}
			for(int x= Engine.Effects.size()-1; x>=0;x--){
				Engine.Effects.get(x).draw(gl);
			}
		
		
	}
	
	/*
	public void HandleTouch(MotionEvent ev){
		
		
		float x, y;
		x = ev.getX(); y = ev.getY();
		
		
		int action = ev.getAction() & MotionEvent.ACTION_MASK;
		if(action == MotionEvent.ACTION_UP || action==MotionEvent.ACTION_POINTER_UP){
			fingerUp = true;
			inc=0;
			mode=0;
			return;
		}else{
			fingerUp = false;
		}
		if(fingerUp){
			return;
		}
				
		switch(mode){
		case 0: //no mode yet
			if(Destroy(x, y)){
				mode=2; //destroy
				return;
			}
			if(Adding(x,y)){
				mode=1; //create
				return;
			}
		case 1: //adding
			this.Adding(x,y);
			return;
		case 2: //destroying
			this.Destroy(x,y);
			return;
		}
		
		
	}
	*/

	public void HandleTouch(Pointer po){
		/*
		final int aid = ev.getActionIndex();
		final int action = ev.getAction();
		final int actionCode = action & MotionEvent.ACTION_MASK;
		Pointer ap;
		ap = Engine.GetPointer(ev.getPointerId(aid));
		if(actionCode == MotionEvent.ACTION_UP || actionCode==MotionEvent.ACTION_POINTER_UP){
			ap.Mode=StarRenderer.MODE_FINGER_UP;
		}else{
			if(ap.Mode==StarRenderer.MODE_FINGER_UP){
				ap.Mode=StarRenderer.MODE_NONE;
			}
		}
		*/

		//for(int i=0;i<ev.getPointerCount();i++){
			//po.x = ev.getX(i);
			//po.y = ev.getY(i);
			
			switch(po.Mode){
			case MODE_NONE: //no mode yet
				Destroy(po);
				if(po.Mode==MODE_NONE){
					Adding(po);
				}
				break;
			case MODE_ADDING: //adding
				this.Adding(po);
				break;
			case MODE_DESTROY: //destroying
				this.Destroy(po);
				break;
			case MODE_FINGER_UP: //finger up
				po.Inc = 0;
				break;
			}
		//}
		
	}
	
	

	
}
	

